//-----------------------------------------------------------------------------
//
// CodeXS - Copyright(c) Businessware Architects cc
// Contact: Willem J Fourie
// E-mail : willemf@bware.biz
//
// This file is part of CodeXS.
//
// CodeXS is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License (GPL) as 
// published by the Free Software Foundation version 2 of the License.
//
// CodeXS is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with CodeXS; if not, write to:
//
// Free Software Foundation, Inc.
// 59 Temple Place
// Suite 330
// Boston
// MA  02111-1307  USA
//
//-----------------------------------------------------------------------------

using System;
using System.Text.RegularExpressions;
using System.CodeDom;
using System.Collections;
using System.Reflection;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Resources;
using CodeXS.Schema;

//#if NET_2_0
using System.Xml.Serialization.Advanced;
//#endif // NET_2_0

namespace CodeXS.Schema.CodeModifier
{
	public class ArrayType
	{
		// Returns the parentheses for a single dimension.
		// CHANGE: 2006-06-27:	 Version 0.57 Beta
		// Chnaged method to porperty
		static public string ArrayParentheses
		{
			get { return "[]"; }
		}
	}
	public class ReferenceType
	{
		static public int GetArrayDimension(CodeTypeReference ReferenceType)
		{
			int dim = 0;
			while(ReferenceType.ArrayRank >= 1)
			{
				++dim;
				ReferenceType = ReferenceType.ArrayElementType;
			}
			return dim;
		}
		static public string GetStringElementType(CodeTypeReference ReferenceType)
		{
			if(ReferenceType.ArrayRank > 0)
			{
				ReferenceType = ReferenceType.ArrayElementType;
			}
			return GetStringType(ReferenceType);
		}
		static public string GetStringType(CodeTypeReference ReferenceType)
		{
			string arrayRank = ArrayType.ArrayParentheses;
			string rankSuffix = "";
			if(ReferenceType.ArrayRank >= 1)
			{
				rankSuffix += arrayRank;
				ReferenceType = ReferenceType.ArrayElementType;
				while(ReferenceType.ArrayRank >= 1)
				{
					rankSuffix += arrayRank;
					ReferenceType = ReferenceType.ArrayElementType;
				}
			}
			return ReferenceType.BaseType + rankSuffix;
		}
	}
	public class Reference
	{
		static public CodeTypeDeclaration GetDeclaredType(CodeGenerator Generator,
			string ClassName)
		{
			IDictionaryEnumerator e = Generator.CodeNamespaces.GetEnumerator();
			while(e.MoveNext())
			{
				CodeNamespace ns = e.Value as CodeNamespace;
				if(ns == null) continue;
				foreach(CodeTypeDeclaration type in ns.Types)
				{
					if(type.Name == ClassName) return type;
				}
			}
			return null;
		}
	}
	public class Member
	{
		// CHANGE: 2006-06-29:	 Version 0.57 Beta
		// Moved Schema.CodeModifer.Member and made 'public static'
		static public bool UseOverrideModifier(CodeNamespace globalNamespace,
			CodeTypeDeclaration decl)
		{
			if (decl.BaseTypes.Count == 0) return false;
			foreach (CodeTypeReference reference in decl.BaseTypes)
			{
				// Check against global namespace types
				foreach (CodeTypeDeclaration type in globalNamespace.Types)
				{
					if (type.Name == reference.BaseType) return true;
				}
			}
			return false;
		}
		static public bool UseOverrideModifier(CodeNamespace nameSpace,
			CodeTypeDeclaration decl, string memberName)
		{
			const MemberAttributes notOverridable =
				~(MemberAttributes.Abstract |
				MemberAttributes.Assembly |
				MemberAttributes.Family |
				MemberAttributes.FamilyAndAssembly |
				MemberAttributes.FamilyOrAssembly |
				MemberAttributes.Override |
				MemberAttributes.Public);
			if (decl.BaseTypes.Count == 0) return false;
			foreach (CodeTypeReference reference in decl.BaseTypes)
			{
				// Check against global namespace types
				foreach (CodeTypeDeclaration type in nameSpace.Types)
				{
					if (type.Name == reference.BaseType)
					{
						if(UseOverrideModifier(nameSpace, type, memberName)) 
							return true;
						foreach (CodeTypeMember typeMember in type.Members)
						{
							if (typeMember.Name == memberName)
							{
								if ((typeMember.Attributes & notOverridable) != 0)
									return false;
								return true;
							}
						}
						return false;
					}
				}
			}
			return false;
		}
		public static void SetOverrideModifiers(CodeNamespace nameSpace)
		{
			foreach (CodeTypeDeclaration type in nameSpace.Types)
			{
				foreach (CodeTypeMember member in type.Members)
				{
					if (UseOverrideModifier(nameSpace, type, member.Name))
					{
						member.Attributes |= MemberAttributes.Override;
					}
				}
			}
		}
	}
	public class CodeSerializationAttribute
	{
		private const string referencedArg = "Referenced";
		private const string referencedAttr = "CodeXS.Class";
		static public void SetReferenced(CodeTypeMember Member, bool Referenced)
		{
			CodeAttributeArgument arg = CreateArgument(referencedArg, 
				Referenced.ToString(), false);
			CodeAttributeDeclaration attr = 
				CreateAttribute(referencedAttr, new CodeAttributeArgument[]{arg});
			SetAttribute(Member, attr, -1);
		}
		static public bool IsReferenced(CodeTypeMember Member)
		{
			bool referenced = false;
			CodeAttributeDeclarationCollection attrs = GetAttribute(Member, referencedAttr);
			// Hopefully, maximum of one...
			if( (attrs.Count > 0) &&
				(attrs[0].Arguments.Count > 0) )
			{
				CodeAttributeArgument arg = GetArgument(attrs[0], referencedArg);
				if(arg != null)
				{
					referenced = (GetArgumentValue(arg) == true.ToString()) ? true : false;
				}
			}
			return referenced;
		}
		static public void RemoveReferenced(CodeTypeMember Member)
		{
			CodeAttributeDeclarationCollection attrs = GetAttribute(Member, referencedAttr);
			foreach(CodeAttributeDeclaration attr in attrs)
			{
				RemoveAttribute(Member, attr);
			}
		}
		// CHANGE: 2006-06-19:	  Version 0.56 Beta
		// Added to remove XmlEnumAttribute("##any:").
		// Test with RSS2.0 schema.
		static public void FixXmlEnumAttribute(CodeTypeDeclaration type)
		{
			const string itemChoice = "ItemChoice";
			const string itemsChoice = "ItemsChoice";
			const string anyAtribute = "##any:";
			if (type.IsEnum &&
				(type.Name.StartsWith(itemChoice) || 
					type.Name.StartsWith(itemsChoice)))
			{
				ArrayList itemMembers = new ArrayList();
				foreach(CodeTypeMember member in type.Members)
				{
					CodeAttributeDeclarationCollection attrs = GetAttribute(member,
						typeof(XmlEnumAttribute));
					foreach (CodeAttributeDeclaration attr in attrs)
					{
						if (attr.Arguments.Count > 0)
						{
							if (GetArgumentValue(attr.Arguments[0]) == anyAtribute)
							{
								itemMembers.Add(member); 
							}
						}
					}
				}
				foreach(CodeTypeMember member in itemMembers)
				{
					type.Members.Remove(member);
				}
			}
		}
		// CHANGE: 2006-06-19:	  Version 0.56 Beta
		// Added to explicitly fix XmlChoiceIdentifierAttribute names, remove
		// XmlAnyElementAttribute attribute.
		static public void FixXmlChoiceIdentifierAttribute(CodeTypeMember member)
		{
			const string itemsElementName = "ItemsElementName";
			CodeAttributeDeclarationCollection attrs = GetAttribute(member,
				typeof(XmlChoiceIdentifierAttribute));
			if(attrs.Count > 0)
			{
				// Modify "ItemsElementName" to "_ItemsElementName".
				foreach (CodeAttributeDeclaration attr in attrs)
				{
					if (attr.Arguments.Count > 0)
					{
						if (GetArgumentValue(attr.Arguments[0]) == itemsElementName)
						{
							attr.Arguments[0].Value = new CodePrimitiveExpression(
								"_" + itemsElementName);
						}
					}
				}
				// Remove the XmlAnyElementAttribute attribute.
				// Test with RSS2.0 schema.
				attrs = GetAttribute(member,
					typeof(XmlAnyElementAttribute));
				while(attrs.Count > 0)
				{
					CodeAttributeDeclaration attr = attrs[0];
					RemoveAttribute(member, attr);
					attrs.RemoveAt(0);
				}
			}
		}
		static public bool FlagIfRoot(CodeGenerator Generator, CodeTypeMember Member)
		{
			bool referenced = false;
			CodeAttributeDeclarationCollection attributes = 
				CodeSerializationAttribute.GetAttribute(
					Member, typeof(XmlRootAttribute));
			if(attributes.Count > 0)
			{
				IDictionaryEnumerator e = Generator.CodeNamespaces.GetEnumerator();
				while(e.MoveNext())
				{
					CodeNamespace ns = e.Value as CodeNamespace;
					if(ns == null) continue;
					foreach(CodeTypeDeclaration type in ns.Types)
					{
						if(type.IsEnum) continue;
						foreach(CodeTypeMember member in type.Members)
						{
							// Process fields only.
							if(member is CodeMemberField)
							{
								CodeMemberField field = member as CodeMemberField;
								if(Member.Name == field.Type.BaseType)
								{
									referenced = true;
									break;
								}
							}
						}
						if(referenced) break;
					}
					if(referenced) break;
				}
			}
			SetReferenced(Member, referenced);
			return referenced;
		}
		static public void SetAttributeElementName(
			CodeAttributeDeclaration Attr, string Name)
		{
			CodeAttributeArgument arg = null;
			foreach(CodeAttributeArgument a in Attr.Arguments)
			{
				if( (a.Name == "ElementName") ||
					(a.Name == "") ||
					(a.Name == null) )
				{
					arg = a;
					break;
				}
			}
			if(arg == null)
			{
				arg = new CodeAttributeArgument(
					"", new CodePrimitiveExpression(Name));
				Attr.Arguments.Insert(0, arg);
			}
		}
		static public void SetNamedAttribute(CodeTypeMember Member,
			Type AttrType)
		{
			// Check for the attribute type: if it exists
			// make sure the name is set and return.
			CodeAttributeDeclarationCollection attrs = 
				CodeSerializationAttribute.GetAttribute(Member,
				 AttrType);
			if(attrs.Count > 0)
			{
				foreach(CodeAttributeDeclaration d in attrs)
				{
					SetAttributeElementName(d, Member.Name);
				}
				return;
			}
			CodeAttributeDeclaration decl = CodeSerializationAttribute.
				CreateAttribute(AttrType, null);
			CodeAttributeArgument arg = new CodeAttributeArgument("",
				new CodePrimitiveExpression(Member.Name));
			decl.Arguments.Insert(0, arg);
			CodeSerializationAttribute.SetAttribute(Member, decl, 0);
		}
		static public bool UpdateXmlElementAttribute(
			CodeNamespace GlobalNamespace,
			CodeTypeMember Member)
		{
			// Check for the XmlElementAttribute: if it exists
			// make sure the name is set and return.
			CodeAttributeDeclarationCollection attrs = 
				CodeSerializationAttribute.GetAttribute(Member,
				typeof(XmlElementAttribute));
			if(attrs.Count > 0)
			{
				foreach(CodeAttributeDeclaration d in attrs)
				{
					SetAttributeElementName(d, Member.Name);
				}
				return true;
			}
			return false;
		}
		static public bool UpdateXmlArrayAttribute(
			CodeNamespace GlobalNamespace,
			CodeTypeMember Member)
		{
			// Check for the XmlArrayAttribute: if it exists
			// make sure the name is set and return.
			CodeAttributeDeclarationCollection attrs = 
				CodeSerializationAttribute.GetAttribute(Member,
				typeof(XmlArrayAttribute));
			if(attrs.Count > 0)
			{
				foreach(CodeAttributeDeclaration d in attrs)
				{
					SetAttributeElementName(d, Member.Name);
				}
				return true;
			}
			return false;
		}
		static public void ModifySerializationAttribute(
			CodeNamespace GlobalNamespace,
			CodeTypeMember Member)
		{
			// Check for the XmlArrayAttribute: if it exists
			// make sure the name is set and return.
			if(UpdateXmlArrayAttribute(GlobalNamespace, Member)) return;
			// Check for the XmlElementAttribute: if it exists
			// make sure the name is set and return.
			if(UpdateXmlElementAttribute(GlobalNamespace, Member)) return;
			// Test for other serialization attributes. If none,
			// add a new XmlElementAttribute, unless we have an
			// XmlArrayItemAttribute - in this case add an
			// XmlArrayAttribute for the collection which is to
			// be serialized as an array.
			Type attrType = typeof(XmlElementAttribute);
			foreach(CodeAttributeDeclaration d in Member.CustomAttributes)
			{
				switch(d.Name)
				{
					// Add an XmlArrayAttribute for the collection which 
					// is to be serialized as an array.
					case "System.Xml.Serialization.XmlArrayItemAttribute":
						attrType = typeof(XmlArrayAttribute);
						break;
					case "System.Xml.Serialization.XmlRootAttribute":
					case "System.Xml.Serialization.XmlAnyAttributeAttribute":
					case "System.Xml.Serialization.XmlAnyElementAttribute":
					case "System.Xml.Serialization.XmlArrayAttribute":
					case "System.Xml.Serialization.XmlAttributeAttribute":
					case "System.Xml.Serialization.XmlChoiceIdentifierAttribute":
					case "System.Xml.Serialization.XmlElementAttribute":
					case "System.Xml.Serialization.XmlEnumAttribute":
					case "System.Xml.Serialization.XmlIgnoreAttribute":
					case "System.Xml.Serialization.XmlIncludeAttribute":
					case "System.Xml.Serialization.XmlTextAttribute":
					case "System.Xml.Serialization.XmlTypeAttribute":
						return;
				}
			}
			SetNamedAttribute(Member, attrType);
		}
		static public CodeAttributeArgument CreateArgument(string ArgumentName,
			string ArgumentValue, bool Literal)
		{
			CodeExpression expression = null;
			if(Literal)
			{
				expression = new CodeSnippetExpression(ArgumentValue);
			}
			else
			{
				expression = new CodePrimitiveExpression(ArgumentValue);
			}
			CodeAttributeArgument argument = 
				new CodeAttributeArgument(ArgumentName, expression);
			return argument;
		}
		static public CodeAttributeArgument GetArgument(
			CodeAttributeDeclaration Attribute,
			string ArgumentName)
		{
			foreach(CodeAttributeArgument argument in Attribute.Arguments)
			{
				if(argument.Name == ArgumentName)
				{
					return argument;
				}
			}
			return null;
		}
		static public string GetArgumentValue(CodeAttributeArgument Argument)
		{
			string s = null;
			if(Argument.Value is CodePrimitiveExpression)
			{
				s = ((CodePrimitiveExpression)Argument.Value).Value.ToString();
			}
			return s;
		}
		static public void SetArgument(CodeAttributeDeclaration Attribute, 
			CodeAttributeArgument Argument)
		{
			CodeAttributeArgument a = 
				CodeSerializationAttribute.GetArgument(Attribute, 
					Argument.Name);
			if(a != null)
			{
				int insertIndex = Attribute.Arguments.IndexOf(a);
				Attribute.Arguments.RemoveAt(insertIndex);
				Attribute.Arguments.Insert(insertIndex, Argument);
			}
			else
			{
				Attribute.Arguments.Add(Argument);
			}
		}
		static public CodeAttributeDeclaration CreateAttribute(
			string Name,
			params CodeAttributeArgument[] Arguments)
		{
			CodeAttributeDeclaration attribute = null;
			if(Arguments == null)
			{
				attribute = new CodeAttributeDeclaration(Name);
			}
			else
			{
				attribute = new CodeAttributeDeclaration(Name,
					Arguments);
			}
			return attribute;
		}
		static public CodeAttributeDeclaration CreateAttribute(
			System.Type SerializationType,
			params CodeAttributeArgument[] Arguments)
		{
			return CreateAttribute(SerializationType.FullName, Arguments);
		}
		static public CodeAttributeDeclarationCollection GetAttribute(
			CodeTypeMember MemberType, string Name)
		{
			CodeAttributeDeclarationCollection attributes = 
				new CodeAttributeDeclarationCollection();
			foreach(CodeAttributeDeclaration attribute in 
				MemberType.CustomAttributes)
			{
				if(attribute.Name == Name)
				{
					attributes.Add(attribute);
				}
			}
			return attributes;
		}
		static public CodeAttributeDeclarationCollection GetAttribute(
			CodeTypeMember MemberType, 
			System.Type SerializationType)
		{
			return GetAttribute(MemberType, SerializationType.FullName);
		}
		// CHANGE: 2006-06-19:	  Version 0.56 Beta
		// Added to allow querying for ignored serialization members.
		static public bool HasIgnoreAttribute(CodeTypeMember MemberType)
		{
			CodeAttributeDeclarationCollection collection =
				GetAttribute(MemberType, typeof(XmlIgnoreAttribute));
			return (collection.Count > 0) ? true : false;
		}
		static public void SetAttribute(CodeTypeMember MemberType,
			CodeAttributeDeclaration Attribute, int AddInsertAt)
		{
			CodeAttributeDeclarationCollection attributes =
				CodeSerializationAttribute.GetAttribute(
					MemberType, Attribute.Name);
			if(attributes.Count > 0)
			{
				foreach(CodeAttributeDeclaration a in attributes)
				{
					int insertIndex = MemberType.CustomAttributes.IndexOf(a);
					MemberType.CustomAttributes.RemoveAt(insertIndex);
					MemberType.CustomAttributes.Insert(insertIndex,
						Attribute);
				}
			}
			else
			{
				if(AddInsertAt == -1)
					MemberType.CustomAttributes.Add(Attribute);
				else
					MemberType.CustomAttributes.Insert(AddInsertAt, Attribute);
			}
		}
		static public void RemoveAttribute(CodeTypeMember MemberType,
			CodeAttributeDeclaration Attribute)
		{
			MemberType.CustomAttributes.Remove(Attribute);
		}
	}
	public class ReplaceableParameter
	{
		private CodeGenerator _codeGenerator = null;
		private IReplaceParameter _replaceParameter = null;
		private string[] FindParameters(string Source)
		{
			Regex regex = new Regex(@"(\b(__)\w+(__)\b)", RegexOptions.None);
			MatchCollection matches = regex.Matches(Source);
			ArrayList parms = new ArrayList();
			foreach(Match match in matches)
			{
				parms.Add(match.Value);
			}
			return (string[])parms.ToArray(typeof(string));
		}
		public string Replace(string Source)
		{
			string[] parms = this.FindParameters(Source);
			foreach(string parm in parms)
			{
				object replace = _codeGenerator.ReplaceableParameter(parm);
				string result = _replaceParameter.Replace(
					_codeGenerator, parm, replace);
				Source = Source.Replace(parm, result);
			}
			return Source;
		}
		private string GetGeneratorList(CodeGenerator CodeGenerator)
		{
			Documentation doc = new Documentation();
			string listItemTemplate = doc.GetDocumentSection(
				Defaults.DocumentationTag + "generator.entry");
			if(listItemTemplate == null) return null;
			string list = "";
			foreach(ICodeModifier modifier in CodeGenerator.CodeModifiers)
			{
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__generator_asm__.ToString(),
						modifier);
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__generator_name__.ToString(),
						modifier);
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__generator_version__.ToString(),
						modifier);
				string listItem = this.Replace(listItemTemplate);
				listItem = Documentation.RemoveBlockDelimiters(listItem);
				list = Documentation.AddLineToBlock(list, listItem);
			}
			list = Documentation.RemoveBlockDelimiters(list);
			return list;
		}
		private string GetGeneratorAssembly(object Name, object Value,
			bool FullPath)
		{
			return Schema.Defaults.ModifierAssembly(Value, FullPath);
		}
		private string GetGeneratorName(object Name, object Value)
		{
			if( (Value != null) && (Value is ICodeModifier) )
			{
				return ((ICodeModifier)Value).Name;
			}
			else return Name.ToString();
		}
		private string GetGeneratorVersion(object Name, object Value)
		{
			if( (Value != null) && (Value is ICodeModifier) )
			{
				return ((ICodeModifier)Value).Version;
			}
			else return Name.ToString();
		}
		public ReplaceableParameter(CodeGenerator CodeGenerator, 
			IReplaceParameter ReplaceParameter)
		{
			_codeGenerator = CodeGenerator;
			_replaceParameter = ReplaceParameter;
		}
	}
	public class CodeSnippetConstruct
	{
		static public CodeSnippetStatement[] CreateCodeBlock(string Code)
		{
			ArrayList codeArray = new ArrayList();
			string[] codeLines = Documentation.SplitBlock(Code);
			foreach(string codeLine in codeLines)
			{
				codeArray.Add(new CodeSnippetStatement(codeLine));
			}
			return (CodeSnippetStatement[])
				codeArray.ToArray(typeof(CodeSnippetStatement));
		}
		static public CodeSnippetStatement[] CreateCodeBlock(
			Assembly Assembly, string ResourceName, 
			ReplaceableParameter Replace)
		{
			foreach(string resName in Assembly.GetManifestResourceNames()) 
			{
				int findIndex = resName.IndexOf(ResourceName);
				if( (findIndex == -1) ||
					(resName.Substring(findIndex) != ResourceName) ) continue;
				StreamReader sr = null;
				string codeBlock = null;
				try
				{
					Stream s = Assembly.GetManifestResourceStream(resName);
					sr = new StreamReader(s);
					codeBlock = sr.ReadToEnd();
					if(Replace != null)
					{
						codeBlock = Replace.Replace(codeBlock);
					}
				}
				catch(Exception e)
				{
					Diagnostics.Log.DefaultLog.LogException(e);
				}
				finally
				{
					if(sr != null) sr.Close();
				}
				return CodeSnippetConstruct.CreateCodeBlock(codeBlock);
			}
			return null;
		}
	}
	public class Property
	{
		static public void AddIgnoreAttribute(
			CodeNamespace GlobalNamespace,
			CodeMemberProperty Property)
		{
			CodeAttributeDeclaration decl = CodeSerializationAttribute.
				CreateAttribute(typeof(XmlIgnoreAttribute), null);
			CodeSerializationAttribute.SetAttribute(Property, decl, -1); 
		}
		static public bool IsPrimitive(Type Type)
		{
			return IsPrimitive(Type.FullName);
		}
		static public bool IsPrimitive(string Type)
		{
			switch(Type.Replace("?",""))
			{
				case "System.Char":
				case "System.Byte":
				case "System.Int16":
				case "System.Int32":
				case "System.Int64":
				case "System.UInt16":
				case "System.UInt32":
				case "System.UInt64":
				case "System.Boolean":
				case "System.DateTime":
				case "System.TimeSpan":
				case "System.Decimal":
				case "System.Single":
				case "System.Double":
				case "System.String":
				case "System.Xml.XmlElement":
				case "System.Xml.XmlAttribute":
				case "System.Object":
				case "System.SByte":
					return true;
			}
			return false;
		}
		// CHANGE: 2006-06-29:	 Version 0.57 Beta
		// Check for known instantiable types that can occur.
		public static bool IsInstantiable(Type type)
		{
			return IsInstantiable(type.Name);
		}
		public static bool IsInstantiable(string type)
		{
			// TODO: Fill in the rest as required.
			switch (type)
			{
				case "System.Xml.XmlQualifiedName":
					return true;
			}
			return false;
		}
		static public bool CanInstantiate(CodeNamespace GlobalNamespace, 
			CodeTypeReference Ref, System.CodeDom.CodeTypeMember Member)
		{
			// Check for types that cannot be instanced.
			// All arrays will be instantiated as typed collections.
			if (Ref.ArrayRank == 0)
			{
				// CHANGE: 2006-06-29:	 Version 0.57 Beta
				// Check for explicit instantiable types.
				if (IsInstantiable(Ref.BaseType)) return true;
				// These types cannot (will not) be instantiated...
				if (IsPrimitive(Ref.BaseType)) return false;
				// Check for enums, and abstract classes.
				foreach (CodeTypeDeclaration type in GlobalNamespace.Types)
				{
					if (type.Name == Ref.BaseType)
					{
						if (type.IsClass &&
							((type.TypeAttributes & TypeAttributes.Abstract) != 0))
						{
							return false;
						}
						if (type.IsEnum) return false;
						return true;
					}
				}
				// CHANGE: 2006-06-29:	 Version 0.57 Beta
				// If not found, assume it can instantiated.
				try
				{
					throw new InvalidOperationException("Cannot determine type '" +
						Ref.BaseType + "'. Type assumed instantiable.");
				}
				catch (Exception e)
				{
					Diagnostics.Log.DefaultLog.LogException(e);
				}
			}
			// Return true if collection - collection can always be instantiated.
			return true;
		}
		public enum Accessor { Get, Set, Both};
		static public string GetFieldName(string FieldName)
		{
			// CHANGE: 2006-06-19:	  Version 0.56 Beta
			// Problem with XSD using 'Id' and 'id'..
			//			return "__" + char.ToLower(FieldName[0]) + 
			//				FieldName.Substring(1);
			return "m" + FieldName;
		}
		static public CodeMemberProperty Create(
			CodeNamespace GlobalNamespace,
			CodeNamespace Namespace,
			CodeTypeDeclaration Declarer, string PropertyName,
			string PropertyType, Accessor Access, MemberAttributes Visibility,
			CodeMemberField Field, bool RenameField,
			bool DefaultInstantiate, bool UseScalarType)
		{
			string fieldName = GetFieldName(PropertyName);
			if(Field == null)
			{
				Field = new CodeMemberField(PropertyType, fieldName);
				Field.Attributes = MemberAttributes.Private;
			}
			else
			{
				if( (Field.Name == PropertyName) && RenameField) 
					Field.Name = fieldName;
			}
			// Create the property
			CodeMemberProperty property = new CodeMemberProperty();
			//property.Name = char.ToUpper(PropertyName[0]) + PropertyName.Substring(1);
			property.Name = PropertyName;
            
            if(UseScalarType)
			{
				property.Type = new CodeTypeReference(CodeModifier.ReferenceType.
					GetStringType(Field.Type));
			}
			else
			{
				property.Type = Field.Type;
			}
			property.Attributes = Visibility;
			// Check for types
			bool canInst = Property.CanInstantiate(GlobalNamespace, property.Type,
				property);
			MemberAttributes scope = Visibility & MemberAttributes.ScopeMask;
			// Write set accessor methods
			if( (Access == Accessor.Set) ||
				(Access == Accessor.Both) )
			{
				if(scope != MemberAttributes.Static)
				{
					if(UseScalarType)
					{
						property.SetStatements.Add(
							new CodeAssignStatement(
							new CodeFieldReferenceExpression(
							new CodeThisReferenceExpression(), fieldName),
							new CodeMethodInvokeExpression(
							new CodeTypeReferenceExpression(Field.Type), "FromArray",
							new CodeExpression[] { new CodePropertySetValueReferenceExpression() } )));
					}
					else
					{
						property.SetStatements.Add(
							new CodeAssignStatement(
							new CodeFieldReferenceExpression(
							new CodeThisReferenceExpression(),
							fieldName), 
							new CodePropertySetValueReferenceExpression()));
					}
				}
				else
				{
					property.SetStatements.Add(
						new CodeAssignStatement(
						new CodeFieldReferenceExpression(
						new CodeTypeReferenceExpression(Declarer.Name),
						fieldName), 
						new CodePropertySetValueReferenceExpression()));
				}
			}
			// Write get accessor methods
			if( (Access == Accessor.Get) ||
				(Access == Accessor.Both) )
			{
				if(scope != MemberAttributes.Static)
				{
					if(UseScalarType)
					{
						property.GetStatements.Add(
							new CodeMethodReturnStatement(
							new CodeMethodInvokeExpression(
							new CodeTypeReferenceExpression(Field.Type), "ToArray",
							new CodeExpression[] 
							{ new CodeFieldReferenceExpression(
								new CodeThisReferenceExpression(), fieldName) } )));
					}
					else
					{
						if(canInst && DefaultInstantiate)
						{
							CodeConditionStatement ifNotExist = 
								new CodeConditionStatement(
								new CodeBinaryOperatorExpression(
								new CodeFieldReferenceExpression(
								new CodeThisReferenceExpression(), fieldName),
								CodeBinaryOperatorType.IdentityEquality,
								new CodePrimitiveExpression(null)));
							property.GetStatements.Add(ifNotExist);
							ifNotExist.TrueStatements.Add(
								new CodeAssignStatement(
								new CodeFieldReferenceExpression(
								new CodeThisReferenceExpression(), fieldName),
								new System.CodeDom.CodeObjectCreateExpression(property.Type,
								new CodeExpression[0])));
						}
						property.GetStatements.Add(
							new CodeMethodReturnStatement(
							new CodeFieldReferenceExpression(
							new CodeThisReferenceExpression(), fieldName)));
					}
				}
				else
				{
					if(canInst && DefaultInstantiate)
					{
						CodeConditionStatement ifNotExist = 
							new CodeConditionStatement(
							new CodeBinaryOperatorExpression(
							new CodeFieldReferenceExpression(
							new CodeTypeReferenceExpression(Declarer.Name), fieldName),
							CodeBinaryOperatorType.IdentityEquality,
							new CodePrimitiveExpression(null)));
						property.GetStatements.Add(ifNotExist);
						ifNotExist.TrueStatements.Add(
							new CodeAssignStatement(
							new CodeFieldReferenceExpression(
							new CodeTypeReferenceExpression(Declarer.Name), fieldName),
							new System.CodeDom.CodeObjectCreateExpression(property.Type,
							new CodeExpression[0])));
					}
					property.GetStatements.Add(
						new CodeMethodReturnStatement(
						new CodeFieldReferenceExpression(
						new CodeTypeReferenceExpression(Declarer.Name), fieldName)));
				}
			}
			return property;
		}
	}
	public class HeadComment : ICodeModifier
	{
		#region ICodeModifier Members
		public string Name
		{
			get { return Schema.Defaults.ModifierName(this); }
		}
		public string Version
		{
			get { return Schema.Information.Version(); }
		}
		public bool Enabled
		{
			get { return true; }
		}
		public bool RunOnce
		{
			get { return false; }
		}
		public void Execute(CodeGenerator CodeGenerator)
		{
			ReplaceableParameter replace = 
				new ReplaceableParameter(CodeGenerator, 
					new ReplaceParameterHandler());
			CodeCommentStatement[] commentBlock = 
				Documentation.CreateCommentBlock(
				Assembly.GetExecutingAssembly(), 
				Defaults.DocumentationFileName, "head",
				replace, false);
			CodeGenerator.CodeNamespace.Comments.AddRange(commentBlock);
		}
		public void Initialize(CodeGenerator CodeGenerator) { }
		public void Deinitialize(CodeGenerator CodeGenerator) { }
		public object SchemaImporterExtension
		{ get { return null; } }
		#endregion
	}
	public class ReplaceParameterHandler : IReplaceParameter
	{
		public enum Parameters
		{
			__file__,
			__generation__,
			__xsd_filepath__,
			__xsd_namespace__,
			__generator_asm__,
			__generator_list__,
			__generator_name__,
			__generator_version__,
			__now__,
			__dotnet_version__,
			__codexs_version__,
		}
		private string GetFileName(CodeGenerator CodeGenerator)
		{
			FileInfo fi = new FileInfo(CodeGenerator.CodeFilePath);
			return fi.Name;
		}
		private string GetSchemaFilePath(CodeGenerator CodeGenerator)
		{
			string uriPath = CodeGenerator.GetSchemaFilePath(
				CodeGenerator.CodeFilePath);
			if( (uriPath == null) || uriPath.StartsWith("local:") )
			{
				uriPath = "N/A";
			}
			return uriPath;
		}
		private string GetSchemaNamespace(CodeGenerator CodeGenerator)
		{
			string ns = CodeGenerator.GetSchemaNamespace(
				CodeGenerator.CodeFilePath);
			if(ns == null) ns = "N/A";
			return ns;
		}
		private string GetGeneration(CodeGenerator CodeGenerator)
		{
			int generationNo = 1;
			string findString = "Generation No.: ";
			StreamReader sr = null;
			try
			{
				FileInfo fileInfo = new FileInfo(CodeGenerator.CodeFilePath);
				if (fileInfo.Exists)
				{
					sr = new StreamReader(CodeGenerator.CodeFilePath);
					string line = null;
					while ((line = sr.ReadLine()) != null)
					{
						int findIndex = -1;
						if ((findIndex = line.IndexOf(findString)) >= 0)
						{
							generationNo =
								System.Convert.ToInt32(line.Substring(
								findIndex + findString.Length)) + 1;
							break;
						}
					}
				}
			}
			finally
			{
				if(sr != null) sr.Close();
			}
			return generationNo.ToString("0000");
		}
		private string GetDateTime()
		{
			return System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
		}
		private string GetGeneratorList(CodeGenerator CodeGenerator)
		{
			Documentation doc = new Documentation();
			string listItemTemplate = doc.GetDocumentSection(
				Defaults.DocumentationTag + "generator.entry");
			if(listItemTemplate == null) return null;
			string list = "";
			foreach(ICodeModifier modifier in CodeGenerator.CodeModifiers)
			{
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__generator_asm__.ToString(),
					modifier);
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__generator_name__.ToString(),
					modifier);
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__generator_version__.ToString(),
					modifier);
				ReplaceableParameter replace = new ReplaceableParameter(
					CodeGenerator, this as IReplaceParameter);
				string listItem = replace.Replace(listItemTemplate);
				listItem = Documentation.RemoveBlockDelimiters(listItem);
				list = Documentation.AddLineToBlock(list, listItem);
			}
			list = Documentation.RemoveBlockDelimiters(list);
			return list;
		}
		private string GetGeneratorAssembly(object Type, bool FullPath)
		{
			return Defaults.ModifierAssembly(Type, FullPath);
		}
		private string GetGeneratorName(object Modifier)
		{
			if( (Modifier != null) && (Modifier is ICodeModifier) )
				return ((ICodeModifier)Modifier).Name;
			return null;
		}
		private string GetGeneratorVersion(object Modifier)
		{
			if( (Modifier != null) && (Modifier is ICodeModifier) )
				return ((ICodeModifier)Modifier).Version;
			return null;
		}
		#region IReplaceParameter Members
		public string Replace(CodeGenerator CodeGenerator, string Name,
			object Value)
		{
			string replace = Name;
			if(Name == Parameters.__file__.ToString())
				replace = this.GetFileName(CodeGenerator);
			if(Name == Parameters.__xsd_filepath__.ToString())
				replace = this.GetSchemaFilePath(CodeGenerator);
			if(Name == Parameters.__xsd_namespace__.ToString())
				replace = this.GetSchemaNamespace(CodeGenerator);
			if(Name == Parameters.__generation__.ToString())
				replace = this.GetGeneration(CodeGenerator);
			if(Name == Parameters.__generator_asm__.ToString())
				replace = this.GetGeneratorAssembly(Value, false);
			if(Name == Parameters.__generator_list__.ToString())
				replace = this.GetGeneratorList(CodeGenerator);
			if(Name == Parameters.__generator_name__.ToString())
				replace = this.GetGeneratorName(Value);
			if(Name == Parameters.__generator_version__.ToString())
				replace = this.GetGeneratorVersion(Value);
			if(Name == Parameters.__now__.ToString())
				replace = this.GetDateTime();
			if(Name == Parameters.__dotnet_version__.ToString())
				replace = System.Environment.Version.ToString();
			if(Name == Parameters.__codexs_version__.ToString())
				replace = CodeGenerator.Version;
			return replace == null ? Name : replace;
		}
		public bool Enabled
		{
			get { return true; }
		}
		#endregion
	}
}
